{
 "cells": [
  {
   "cell_type": "raw",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "---\n",
    "title: \"Parameterization: compile-time metaprogramming\"\n",
    "description: An introduction to parameters and compile-time metaprogramming.\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Many languages have facilities for _metaprogramming_: that is, for writing code that generates or modifies code. Python has facilities for dynamic metaprogramming: features like decorators, metaclasses, and many more. These features make Python very flexible and productive, but since they're dynamic, they come with runtime overhead. Other languages have static or compile-time metaprogramming features, like C preprocessor macros and C++ templates. These can be limiting and hard to use.\n",
    "\n",
    "To support Modular's work in AI, Mojo aims to provide powerful, easy-to-use metaprogramming with zero runtime cost. This compile-time metaprogramming uses the same language as runtime programs, so you don't have to learn a new language—just a few new features.\n",
    "\n",
    "The main new feature is _parameters_. You can think of a parameter as a compile-time variable that becomes a runtime constant. This usage of \"parameter\" is probably different from what you're used to from other languages, where \"parameter\" and \"argument\" are often used interchangeably. In Mojo, \"parameter\" and \"parameter expression\" refer to compile-time values, and \"argument\" and \"expression\" refer to runtime values. \n",
    "\n",
    "In Mojo, you can add parameters to a struct or function. You can also define \n",
    "named parameter expressions—aliases—that you can use as runtime constants."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Parameterized functions\n",
    "\n",
    "To define a _parameterized function_, add parameters in square brackets ahead\n",
    "of the argument list. Each parameter is formatted just like an argument: a \n",
    "parameter name, followed by a colon and a type (which is required). In the\n",
    "following example, the function has a single parameter, `count` of type `Int`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn repeat[count: Int](msg: String):\n",
    "    @parameter\n",
    "    for i in range(count):\n",
    "        print(msg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The [`@parameter`](/mojo/manual/decorators/parameter) directive shown here \n",
    "causes the `for` loop to be evaluated at compile time. The directive only works\n",
    "if the loop limits are compile-time constants. Since `count` is a parameter,\n",
    "`range(count)` can be calculated at compile time.\n",
    "\n",
    "Calling a parameterized function, you provide values for the parameters, just \n",
    "like function arguments: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello\n",
      "Hello\n",
      "Hello\n"
     ]
    }
   ],
   "source": [
    "repeat[3](\"Hello\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " \n",
    " The compiler resolves the parameter values during compilation, and creates a\n",
    " concrete version of the `repeat[]()` function for each unique parameter value.\n",
    " After resolving the parameter values and unrolling the loop, the `repeat[3]()`\n",
    " function would be roughly equivalent to this:\n",
    "\n",
    "```mojo\n",
    "fn repeat_3(msg: String):\n",
    "    print(msg)\n",
    "    print(msg)\n",
    "    print(msg)\n",
    "```\n",
    "\n",
    ":::note\n",
    "\n",
    "This doesn't represent actual code generated by the compiler. By the\n",
    "time parameters are resolved, Mojo code has already been transformed to an\n",
    "intermediate representation in [MLIR](https://mlir.llvm.org/).\n",
    "\n",
    ":::\n",
    "\n",
    "If the compiler can't resolve all parameter values to constant values, \n",
    "compilation fails."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Parameters and generics\n",
    "\n",
    "\"Generics\" refers to functions that can act on multiple types of values, or \n",
    "containers that can hold multiple types of values. For example, \n",
    "[`List`](/mojo/stdlib/collections/list/List), can hold\n",
    "different types of values, so you can have a list of `Int` values, or\n",
    "a list of `String` values).\n",
    "\n",
    "In Mojo, generics use parameters to specify types. For example, `List`\n",
    "takes a type parameter, so a vector of integers is written `List[Int]`.\n",
    "So all generics use parameters, but **not** everything that uses parameters is a\n",
    "generic. \n",
    "\n",
    "For example, the `repeat[]()` function in the previous section includes \n",
    "parameter of type `Int`, and an argument of type `String`. It's parameterized,\n",
    "but not generic. A generic function or struct is parameterized on _type_. For\n",
    "example, we could rewrite `repeat[]()` to take any type of argument that\n",
    "conforms to the [`Stringable`](/mojo/stdlib/builtin/str#stringable) trait:  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "42\n",
      "42\n"
     ]
    }
   ],
   "source": [
    "fn repeat[MsgType: Stringable, count: Int](msg: MsgType):\n",
    "    @parameter\n",
    "    for i in range(count):\n",
    "        print(msg)\n",
    "\n",
    "# Must use keyword parameter for `count`\n",
    "repeat[count=2](42)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This updated function takes any `Stringable` type, so you can pass it an `Int`,\n",
    "`String`, or `Bool` value.\n",
    "\n",
    "You can't pass the `count` as a positional keyword without also specifying `MsgType`.\n",
    "You can put `//` after `MsgType` to specify that it's always inferred by the argument. Now\n",
    "you can pass the following parameter `count` positionally:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "42\n",
      "42\n"
     ]
    }
   ],
   "source": [
    "fn repeat[MsgType: Stringable, //, count: Int](msg: MsgType):\n",
    "    @parameter\n",
    "    for i in range(count):\n",
    "        print(msg)\n",
    "\n",
    "# MsgType is always inferred, so first positional keyword `2` is passed to `count`\n",
    "repeat[2](42)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "Mojo's support for generics is still early. You can write generic functions like\n",
    "this using traits and parameters. You can also write generic collections like\n",
    "`List` and `Dict`. If you're interested in learning how these types work, you\n",
    "can find the source code for the standard library collection types \n",
    "[on GitHub](https://github.com/modularml/mojo/blob/nightly/stdlib/src/collections/)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## Parameterized structs\n",
    "\n",
    "You can also add parameters to structs. You can use parameterized structs to\n",
    "build generic containers. For example, a generic array type might include code\n",
    "like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "from memory.unsafe_pointer import UnsafePointer, initialize_pointee_copy, destroy_pointee\n",
    "\n",
    "struct GenericArray[ElementType: CollectionElement]:\n",
    "    var data: UnsafePointer[ElementType]\n",
    "    var size: Int\n",
    "\n",
    "    fn __init__(inout self, *elements: ElementType):\n",
    "        self.size = len(elements)\n",
    "        self.data = UnsafePointer[ElementType].alloc(self.size)\n",
    "        for i in range(self.size):\n",
    "            initialize_pointee_move(self.data.offset(i), elements[i])\n",
    "\n",
    "    fn __del__(owned self):\n",
    "        for i in range(self.size):\n",
    "            destroy_pointee(self.data.offset(i))\n",
    "        self.data.free()\n",
    "\n",
    "    fn __getitem__(self, i: Int) raises -> ref [__lifetime_of(self)] ElementType:\n",
    "        if (i < self.size):\n",
    "            return self.data[i]\n",
    "        else:\n",
    "            raise Error(\"Out of bounds\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This struct has a single parameter, `ElementType`, which is a placeholder for\n",
    "the data type you want to store in the array, sometimes called a _type\n",
    "parameter_. `ElementType` is typed as\n",
    "[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement), which is a\n",
    "[trait](/mojo/manual/traits) representing any type that can be copied and moved.\n",
    "\n",
    "As with parameterized functions, you need to pass in parameter values when you\n",
    "use a parameterized struct. In this case, when you create an instance of \n",
    "`GenericArray`, you need to specify the type you want to store, like `Int`, or\n",
    "`Float64`. (This is a little confusing, because the _parameter value_ you're\n",
    "passing in this case is a _type_. That's OK: a Mojo type is a valid compile-time\n",
    "value.)\n",
    "\n",
    "You'll see that `ElementType` is used throughout the struct where you'd usually see a \n",
    "type name. For example, as the formal type for the `elements` in the \n",
    "constructor, and the return type of the `__getitem__()` method.\n",
    "\n",
    "Here's an example of using `GenericArray`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 2 3 4 "
     ]
    }
   ],
   "source": [
    "var array = GenericArray[Int](1, 2, 3, 4)\n",
    "for i in range(array.size):\n",
    "    print(array[i], end=\" \")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A parameterized struct can use the `Self` type to represent a concrete instance\n",
    "of the struct (that is, with all its parameters specified). For example, you\n",
    "could add a static factory method to `GenericArray` with the following\n",
    "signature:\n",
    "\n",
    "```mojo\n",
    "struct GenericArray[ElementType: CollectionElement]:\n",
    "    ...\n",
    "\n",
    "    @staticmethod\n",
    "    fn splat(count: Int, value: ElementType) -> Self:\n",
    "        # Create a new array with count instances of the given value\n",
    "```\n",
    "\n",
    "Here, `Self` is equivalent to writing `GenericArray[ElementType]`. That is, you\n",
    "can call the `splat()` method like this:\n",
    "\n",
    "```mojo\n",
    "GenericArray[Float64].splat(8, 0)\n",
    "```\n",
    "\n",
    "The method returns an instance of `GenericArray[Float64]`."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Case study: the SIMD type\n",
    "\n",
    "For a real-world example of a parameterized type, let's look at the \n",
    "[`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type from Mojo's standard library.\n",
    "\n",
    "[Single instruction, multiple data (SIMD)](https://en.wikipedia.org/wiki/Single_instruction,_multiple_data) is a parallel processing technology built into many modern CPUs,\n",
    "GPUs, and custom accelerators. SIMD allows you to perform a single operation on\n",
    "multiple pieces of data at once. For example, if you want to take the square \n",
    "root of each element in an array, you can use SIMD to parallelize the work. \n",
    "\n",
    "Processors implement SIMD using low-level vector registers in hardware that hold\n",
    "multiple instances of a scalar data type. In order to use the SIMD instructions\n",
    "on these processors, the data must be shaped into the proper SIMD width\n",
    "(data type) and length (vector size). Processors may support 512-bit or\n",
    "longer SIMD vectors, and support many data types from 8-bit integers to 64-bit \n",
    "floating point numbers, so it's not practical to define all of the possible SIMD\n",
    "variations. \n",
    "\n",
    "Mojo's [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type (defined as a struct)\n",
    "exposes the common SIMD operations through its methods, and makes the SIMD data type\n",
    "and size values parametric. This allows you to directly map your data to the \n",
    "SIMD vectors on any hardware.\n",
    "\n",
    "Here's a cut-down (non-functional) version of Mojo's `SIMD` type definition:\n",
    "\n",
    "```mojo\n",
    "struct SIMD[type: DType, size: Int]:\n",
    "    var value: … # Some low-level MLIR stuff here\n",
    "\n",
    "    # Create a new SIMD from a number of scalars\n",
    "    fn __init__(inout self, *elems: SIMD[type, 1]):  ...\n",
    "\n",
    "    # Fill a SIMD with a duplicated scalar value.\n",
    "    @staticmethod\n",
    "    fn splat(x: SIMD[type, 1]) -> SIMD[type, size]: ...\n",
    "\n",
    "    # Cast the elements of the SIMD to a different elt type.\n",
    "    fn cast[target: DType](self) -> SIMD[target, size]: ...\n",
    "\n",
    "    # Many standard operators are supported.\n",
    "    fn __add__(self, rhs: Self) -> Self: ...\n",
    "```\n",
    "\n",
    "So you can create and use a SIMD vector like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 4 9 16 "
     ]
    }
   ],
   "source": [
    "var vector = SIMD[DType.int16, 4](1, 2, 3, 4)\n",
    "vector = vector * vector\n",
    "for i in range(4):\n",
    "    print(vector[i], end=\" \")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see, a simple arithmetic operator like `*` applied to a pair of \n",
    "`SIMD` vector operates on the corresponding elements in each vector.\n",
    "\n",
    "Defining each SIMD variant with parameters is great for code reuse because the\n",
    "`SIMD` type can express all the different vector variants statically, instead of\n",
    "requiring the language to pre-define every variant.\n",
    "\n",
    "Because `SIMD` is a parameterized type, the `self` argument in its functions\n",
    "carries those parameters—the full type name is `SIMD[type, size]`. Although\n",
    "it's valid to write this out (as shown in the return type of `splat()`), this\n",
    "can be verbose, so we recommend using the `Self` type (from\n",
    "[PEP673](https://peps.python.org/pep-0673/)) like the `__add__` example does."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Overloading on parameters\n",
    "\n",
    "Functions and methods can be overloaded on their parameter signatures. The\n",
    "overload resolution logic filters for candidates according to the following\n",
    "rules, in order of precedence:\n",
    "\n",
    "1) Candidates with the minimal number of implicit conversions (in both arguments\n",
    "and parameters).\n",
    "2) Candidates without variadic arguments.\n",
    "3) Candidates without variadic parameters.\n",
    "4) Candidates with the shortest parameter signature.\n",
    "5) Non-`@staticmethod` candidates (over `@staticmethod` ones, if available). \n",
    "\n",
    "If there is more than one candidate after applying these rules, the overload\n",
    "resolution fails. For example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "foo[x: MyInt, a: Int]()\n",
      "bar[a: Int](b: Int)\n",
      "bar[*a: Int](b: Int)\n"
     ]
    }
   ],
   "source": [
    "@register_passable(\"trivial\")\n",
    "struct MyInt:\n",
    "    \"\"\"A type that is implicitly convertible to `Int`.\"\"\"\n",
    "    var value: Int\n",
    "\n",
    "    @always_inline(\"nodebug\")\n",
    "    fn __init__(inout self, _a: Int):\n",
    "        self.value = _a\n",
    "\n",
    "fn foo[x: MyInt, a: Int]():\n",
    "    print(\"foo[x: MyInt, a: Int]()\")\n",
    "\n",
    "fn foo[x: MyInt, y: MyInt]():\n",
    "    print(\"foo[x: MyInt, y: MyInt]()\")\n",
    "\n",
    "fn bar[a: Int](b: Int):\n",
    "    print(\"bar[a: Int](b: Int)\")\n",
    "\n",
    "fn bar[a: Int](*b: Int):\n",
    "    print(\"bar[a: Int](*b: Int)\")\n",
    "\n",
    "fn bar[*a: Int](b: Int):\n",
    "    print(\"bar[*a: Int](b: Int)\")\n",
    "\n",
    "fn parameter_overloads[a: Int, b: Int, x: MyInt]():\n",
    "    # `foo[x: MyInt, a: Int]()` is called because it requires no implicit\n",
    "    # conversions, whereas `foo[x: MyInt, y: MyInt]()` requires one.\n",
    "    foo[x, a]()\n",
    "\n",
    "    # `bar[a: Int](b: Int)` is called because it does not have variadic\n",
    "    # arguments or parameters.\n",
    "    bar[a](b)\n",
    "\n",
    "    # `bar[*a: Int](b: Int)` is called because it has variadic parameters.\n",
    "    bar[a, a, a](b)\n",
    "\n",
    "parameter_overloads[1, 2, MyInt(3)]()\n",
    "\n",
    "struct MyStruct:\n",
    "    fn __init__(inout self):\n",
    "        pass\n",
    "\n",
    "    fn foo(inout self):\n",
    "        print(\"calling instance method\")\n",
    "\n",
    "    @staticmethod\n",
    "    fn foo():\n",
    "        print(\"calling static method\")\n",
    "\n",
    "fn test_static_overload():\n",
    "    var a = MyStruct()\n",
    "    # `foo(inout self)` takes precedence over a static method.\n",
    "    a.foo()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Using parameterized types and functions\n",
    "\n",
    "You can use parametric types and functions by passing values to the\n",
    "parameters in square brackets. For example, for the `SIMD` type above, `type`\n",
    "specifies the data type and `size` specifies the length of the SIMD vector (it\n",
    "must be a power of 2):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "small_vec type: float32 length: 4\n",
      "bigger_vec2 type: float32 length: 32\n"
     ]
    }
   ],
   "source": [
    "# Make a vector of 4 floats.\n",
    "var small_vec = SIMD[DType.float32, 4](1.0, 2.0, 3.0, 4.0)\n",
    "\n",
    "# Make a big vector containing 1.0 in float16 format.\n",
    "var big_vec = SIMD[DType.float16, 32].splat(1.0)\n",
    "\n",
    "# Do some math and convert the elements to float32.\n",
    "var bigger_vec = (big_vec+big_vec).cast[DType.float32]()\n",
    "\n",
    "# You can write types out explicitly if you want of course.\n",
    "var bigger_vec2 : SIMD[DType.float32, 32] = bigger_vec\n",
    "\n",
    "print('small_vec type:', small_vec.element_type, 'length:', len(small_vec))\n",
    "print('bigger_vec2 type:', bigger_vec2.element_type, 'length:', len(bigger_vec2))"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that the `cast()` method also needs a parameter to specify the type you\n",
    "want from the cast (the method definition above expects a `target` parametric\n",
    "value). Thus, just as the `SIMD` struct is a generic type definition, the\n",
    "`cast()` method is a generic method definition. At compile time, the compiler\n",
    "creates a concrete version of the `cast()` method with the target parameter\n",
    "bound to `DType.float32`.\n",
    "\n",
    "The code above shows the use of concrete types (that is, the parameters are all\n",
    "bound to known values). But the major power of parameters comes from the\n",
    "ability to define parametric algorithms and types (code that uses the parameter\n",
    "values). For example, here's how to define a parametric algorithm with `SIMD`\n",
    "that is type- and width-agnostic:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0.154296875, 0.154296875, 0.154296875, 0.154296875]\n"
     ]
    }
   ],
   "source": [
    "from math import sqrt\n",
    "\n",
    "fn rsqrt[dt: DType, width: Int](x: SIMD[dt, width]) -> SIMD[dt, width]:\n",
    "    return 1 / sqrt(x)\n",
    "\n",
    "var v = SIMD[DType.float16, 4](42)\n",
    "print(rsqrt(v))"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice that the `x` argument is actually a `SIMD` type based on the function\n",
    "parameters. The runtime program can use the value of the parameters, because the\n",
    "parameters are resolved at compile-time before they are needed by the runtime\n",
    "program (but compile-time parameter expressions cannot use runtime values).\n",
    "\n",
    "### Parameter inference\n",
    "\n",
    "The Mojo compiler can often _infer_ parameter values, so you don't always have\n",
    "to specify them. For example, you can call the `rsqrt()` function defined above\n",
    "without any parameters:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0.174072265625, 0.174072265625, 0.174072265625, 0.174072265625]\n"
     ]
    }
   ],
   "source": [
    "var v = SIMD[DType.float16, 4](33)\n",
    "print(rsqrt(v))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "The compiler infers its parameters based on the parametric `v`\n",
    "value passed into it, as if you wrote `rsqrt[DType.float16, 4](v)` explicitly.\n",
    "\n",
    "Mojo can also infer the values of struct parameters from the arguments passed to \n",
    "a constructor or static method.\n",
    "\n",
    "For example, consider the following struct:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "@value\n",
    "struct One[Type: StringableCollectionElement]:\n",
    "    var value: Type\n",
    "\n",
    "    fn __init__(inout self, value: Type):\n",
    "        self.value = value\n",
    "\n",
    "def use_one():\n",
    "    s1 = One(123)\n",
    "    s2 = One(\"Hello\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that you can create an instance of `One` without specifying the `Type`\n",
    "parameter—Mojo can infer it from the `value` argument.\n",
    "\n",
    "You can also infer parameters from a parameterized type passed to a constructor\n",
    "or static method:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "infer me\n",
      "🔥 1 2\n"
     ]
    }
   ],
   "source": [
    "struct Two[Type: StringableCollectionElement]:\n",
    "    var val1: Type\n",
    "    var val2: Type\n",
    "\n",
    "    fn __init__(inout self, one: One[Type], another: One[Type]):\n",
    "        self.val1 = one.value\n",
    "        self.val2 = another.value\n",
    "        print(self.val1, self.val2)\n",
    "\n",
    "    @staticmethod\n",
    "    fn fire(thing1: One[Type], thing2: One[Type]):\n",
    "        print(\"🔥\", thing1.value, thing2.value)\n",
    "\n",
    "def use_two():\n",
    "    s3 = Two(One(\"infer\"), One(\"me\"))\n",
    "    Two.fire(One(1), One(2))\n",
    "\n",
    "use_two()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`Two` takes a `Type` parameter, and its constructor takes values of type\n",
    "`One[Type]`. When constructing an instance of `Two`, you don't need to specify\n",
    "the `Type` parameter, since it can be inferred from the arguments.\n",
    "\n",
    "Similarly, the static `fire()` method takes values of type `One[Type]`, so Mojo\n",
    "can infer the `Type` value at compile time.\n",
    "\n",
    ":::note\n",
    "\n",
    "If you're familiar with C++, you may recognize this as similar to Class Template\n",
    "Argument Deduction (CTAD).\n",
    "\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Optional parameters and keyword parameters\n",
    "\n",
    "Just as you can specify [optional\n",
    "arguments](/mojo/manual/functions#optional-arguments) in function signatures,\n",
    "you can also define an optional _parameter_ by giving it a default value.\n",
    "\n",
    "You can also pass parameters by keyword, just like you can use \n",
    "[keyword arguments](/mojo/manual/functions#keyword-arguments).\n",
    "For a function or struct with multiple optional parameters, using keywords\n",
    "allows you to pass only the parameters you want to specify, regardless of\n",
    "their position in the function signature. \n",
    "\n",
    "For example, here's a function with two parameters, each with a default value:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn speak[a: Int = 3, msg: StringLiteral = \"woof\"]():\n",
    "    print(msg, a)\n",
    "\n",
    "fn use_defaults() raises:\n",
    "    speak()             # prints 'woof 3'\n",
    "    speak[5]()          # prints 'woof 5'\n",
    "    speak[7, \"meow\"]()  # prints 'meow 7'\n",
    "    speak[msg=\"baaa\"]() # prints 'baaa 3'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Recall that when a parametric function is called, Mojo can infer the parameter values.\n",
    "That is, it can use the parameter values attached to an argument value (see the `sqrt[]()`\n",
    "example above). If the parametric function also has a default value defined,\n",
    "then the inferred parameter type takes precedence.\n",
    "\n",
    "For example, in the following code, we update the parametric `speak[]()` function\n",
    "to take an argument with a parametric type. Although the function has a default\n",
    "parameter value for `a`, Mojo instead uses the inferred `a` parameter value\n",
    "from the `bar` argument (as written, the default `a` value can never be used,\n",
    "but this is just for demonstration purposes):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "@value\n",
    "struct Bar[v: Int]:\n",
    "    pass\n",
    "\n",
    "fn speak[a: Int = 3, msg: StringLiteral = \"woof\"](bar: Bar[a]):\n",
    "    print(msg, a)\n",
    "\n",
    "fn use_inferred():\n",
    "    speak(Bar[9]())  # prints 'woof 9'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As mentioned above, you can also use optional parameters and keyword \n",
    "parameters in a struct:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "struct KwParamStruct[greeting: String = \"Hello\", name: String = \"🔥mojo🔥\"]:\n",
    "    fn __init__(inout self):\n",
    "        print(greeting, name)\n",
    "\n",
    "fn use_kw_params():\n",
    "    var a = KwParamStruct[]()                 # prints 'Hello 🔥mojo🔥'\n",
    "    var b = KwParamStruct[name=\"World\"]()     # prints 'Hello World'\n",
    "    var c = KwParamStruct[greeting=\"Hola\"]()  # prints 'Hola 🔥mojo🔥'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ":::note\n",
    "\n",
    "Mojo supports positional-only and keyword-only parameters, following the same\n",
    "rules as [positional-only and keyword-only\n",
    "arguments](/mojo/manual/functions#positional-only-and-keyword-only-arguments).\n",
    "\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Infer-only parameters\n",
    "\n",
    "Sometimes you need to declare functions where parameters depend on other\n",
    "parameters. Because the signature is processed left to right, a parameter can\n",
    "only _depend_ on a parameter earlier in the parameter list. For example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Value:  2.2000000000000002\n",
      "Value is floating-point:  True\n"
     ]
    }
   ],
   "source": [
    "fn dependent_type[dtype: DType, value: Scalar[dtype]]():\n",
    "    print(\"Value: \", value)\n",
    "    print(\"Value is floating-point: \", dtype.is_floating_point())\n",
    "\n",
    "dependent_type[DType.float64, Float64(2.2)]()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can't reverse the position of the `dtype` and `value` parameters, because\n",
    "`value` depends on `dtype`. However, because `dtype` is a required parameter,\n",
    "you can't leave it out of the parameter list and let Mojo infer it from `value`:\n",
    "\n",
    "```mojo\n",
    "dependent_type[Float64(2.2)]() # Error!\n",
    "```\n",
    "\n",
    "Infer-only parameters are a special class of parameters that are **always** \n",
    "inferred from context. Infer-only parameters are placed at the **beginning** of\n",
    "the parameter list, set off from other parameters by the `//` sigil`:\n",
    "\n",
    "```mojo\n",
    "fn example[type: CollectionElement, //, list: List[type]]()\n",
    "```\n",
    "\n",
    "Transforming `dtype` into an infer-only parameter solves this problem:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Value:  2.2000000000000002\n",
      "Value is floating-point:  True\n"
     ]
    }
   ],
   "source": [
    "fn dependent_type[dtype: DType, //, value: Scalar[dtype]]():\n",
    "    print(\"Value: \", value)\n",
    "    print(\"Value is floating-point: \", dtype.is_floating_point())\n",
    "\n",
    "dependent_type[Float64(2.2)]()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Because infer-only parameters are declared at the beginning of the parameter\n",
    "list, other parameters can depend on them, and the compiler will always attempt\n",
    "to infer the infer-only values from bound parameters or arguments.\n",
    "\n",
    "If the compiler can't infer the value of an infer-only parameter, compilation\n",
    "fails."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Variadic parameters\n",
    "\n",
    "Mojo also supports variadic parameters, similar to \n",
    "[Variadic arguments](/mojo/manual/functions#variadic-arguments):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "struct MyTensor[*dimensions: Int]:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Variadic parameters currently have some limitations that variadic arguments don't have:\n",
    "\n",
    "- Variadic parameters must be homogeneous—that is, all the values must be the\n",
    "  same type. \n",
    "  \n",
    "- The parameter type must be register-passable.\n",
    "\n",
    "- The parameter values aren't automatically projected into a `VariadicList`, so you\n",
    "  need to construct the list explicitly:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn sum_params[*values: Int]() -> Int:\n",
    "    alias list = VariadicList(values)\n",
    "    var sum = 0\n",
    "    for v in list:\n",
    "        sum += v\n",
    "    return sum"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Variadic keyword parameters (for example, `**kwparams`) are\n",
    "not supported yet."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Parameter expressions are just Mojo code\n",
    "\n",
    "A parameter expression is any code expression (such as `a+b`) that occurs where\n",
    "a parameter is expected. Parameter expressions support operators and function\n",
    "calls, just like runtime code, and all parameter types use the same type\n",
    "system as the runtime program (such as `Int` and `DType`).\n",
    "\n",
    "Because parameter expressions use the same grammar and types as runtime\n",
    "Mojo code, you can use many \n",
    "[\"dependent type\"](https://en.wikipedia.org/wiki/Dependent_type) features. For\n",
    "example, you might want to define a helper function to concatenate two SIMD\n",
    "vectors:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "result type: float32 length: 4\n"
     ]
    }
   ],
   "source": [
    "fn concat[ty: DType, len1: Int, len2: Int](\n",
    "        lhs: SIMD[ty, len1], rhs: SIMD[ty, len2]) -> SIMD[ty, len1+len2]:\n",
    "\n",
    "    var result = SIMD[ty, len1 + len2]()\n",
    "    for i in range(len1):\n",
    "        result[i] = SIMD[ty, 1](lhs[i])\n",
    "    for j in range(len2):\n",
    "        result[len1 + j] = SIMD[ty, 1](rhs[j])\n",
    "    return result\n",
    "\n",
    "var a = SIMD[DType.float32, 2](1, 2)\n",
    "var x = concat(a, a)\n",
    "\n",
    "print('result type:', x.element_type, 'length:', len(x))"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that the resulting length is the sum of the input vector lengths, and this is expressed with a simple `+` operation. \n",
    "\n",
    "### Powerful compile-time programming\n",
    "\n",
    "While simple expressions are useful, sometimes you want to write imperative\n",
    "compile-time logic with control flow. You can even do compile-time recursion.\n",
    "For instance, here is an example \"tree reduction\" algorithm that sums all\n",
    "elements of a vector recursively into a scalar:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3, 4]\n",
      "Elements sum: 10\n"
     ]
    }
   ],
   "source": [
    "fn slice[ty: DType, new_size: Int, size: Int](\n",
    "        x: SIMD[ty, size], offset: Int) -> SIMD[ty, new_size]:\n",
    "    var result = SIMD[ty, new_size]()\n",
    "    for i in range(new_size):\n",
    "        result[i] = SIMD[ty, 1](x[i + offset])\n",
    "    return result\n",
    "\n",
    "fn reduce_add[ty: DType, size: Int](x: SIMD[ty, size]) -> Int:\n",
    "    @parameter\n",
    "    if size == 1:\n",
    "        return int(x[0])\n",
    "    elif size == 2:\n",
    "        return int(x[0]) + int(x[1])\n",
    "\n",
    "    # Extract the top/bottom halves, add them, sum the elements.\n",
    "    alias half_size = size // 2\n",
    "    var lhs = slice[ty, half_size, size](x, 0)\n",
    "    var rhs = slice[ty, half_size, size](x, half_size)\n",
    "    return reduce_add[ty, half_size](lhs + rhs)\n",
    "\n",
    "var x = SIMD[DType.index, 4](1, 2, 3, 4)\n",
    "print(x)\n",
    "print(\"Elements sum:\", reduce_add(x))"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This makes use of the [`@parameter`](/mojo/manual/decorators/parameter) decorator to create a parametric if condition, which is an `if` statement that\n",
    "runs at compile-time. It requires that its condition be a valid parameter\n",
    "expression, and ensures that only the live branch of the `if` statement is\n",
    "compiled into the program. (This is similar to use of the `@parameter` decorator\n",
    "with a `for` loop shown earlier.)\n",
    "\n",
    "## Mojo types are just parameter expressions\n",
    "\n",
    "While we've shown how you can use parameter expressions within types, type\n",
    "annotations can themselves be arbitrary expressions (just like in Python).\n",
    "Types in Mojo have a special metatype type, allowing type-parametric algorithms\n",
    "and functions to be defined. \n",
    "\n",
    "For example, we can create a simplified `Array` that supports arbitrary types of\n",
    "elements (via the `AnyTrivialRegType` parameter):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3.1400001049041748 3.1400001049041748 3.1400001049041748 3.1400001049041748\n"
     ]
    }
   ],
   "source": [
    "struct Array[T: AnyTrivialRegType]:\n",
    "    var data: Pointer[T]\n",
    "    var size: Int\n",
    "\n",
    "    fn __init__(inout self, size: Int, value: T):\n",
    "        self.size = size\n",
    "        self.data = Pointer[T].alloc(self.size)\n",
    "        for i in range(self.size):\n",
    "            self.data[i] = value\n",
    "\n",
    "    fn __getitem__(self, i: Int) -> T:\n",
    "        return self.data[i]\n",
    "\n",
    "    fn __del__(owned self):\n",
    "        self.data.free()\n",
    "\n",
    "var v = Array[Float32](4, 3.14)\n",
    "print(v[0], v[1], v[2], v[3])"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice that the `T` parameter is being used as the formal type for the\n",
    "`value` arguments and the return type of the `__getitem__()` function. \n",
    "Parameters allow the `Array` type to provide different APIs based on the\n",
    "different use-cases. \n",
    "\n",
    "There are many other cases that benefit from more advanced use of parameters.\n",
    "For example, you can execute a closure N times in parallel, feeding in a value\n",
    "from the context, like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn parallelize[func: fn (Int) -> None](num_work_items: Int):\n",
    "    # Not actually parallel: see the 'algorithm' module for real implementation.\n",
    "    for i in range(num_work_items):\n",
    "        func(i)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Another example where this is important is with variadic generics, where an\n",
    "algorithm or data structure may need to be defined over a list of heterogeneous\n",
    "types such as for a tuple. Right now, this is not fully supported in Mojo and \n",
    "requires writing some MLIR by hand. In the future, this will be possible in pure\n",
    "Mojo."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `alias`: named parameter expressions\n",
    "\n",
    "It is very common to want to *name* compile-time values. Whereas `var` defines a\n",
    "runtime value, we need a way to define a\n",
    "compile-time temporary value. For this, Mojo uses an `alias` declaration. \n",
    "\n",
    "For example, the [`DType`](/mojo/stdlib/builtin/dtype/DType) struct \n",
    "implements a simple enum using aliases for the enumerators like this (the actual\n",
    "`DType` implementation details vary a bit):\n",
    "\n",
    "```mojo\n",
    "struct DType:\n",
    "    var value : UI8\n",
    "    alias invalid = DType(0)\n",
    "    alias bool = DType(1)\n",
    "    alias int8 = DType(2)\n",
    "    alias uint8 = DType(3)\n",
    "    alias int16 = DType(4)\n",
    "    alias int16 = DType(5)\n",
    "    ...\n",
    "    alias float32 = DType(15)\n",
    "```\n",
    "\n",
    "This allows clients to use `DType.float32` as a parameter expression (which also\n",
    "works as a runtime value) naturally. Note that this is invoking the\n",
    "runtime constructor for `DType` at compile-time.\n",
    "\n",
    "Types are another common use for aliases. Because types are compile-time\n",
    "expressions, it is handy to be able to do things like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "alias Float16 = SIMD[DType.float16, 1]\n",
    "alias UInt8 = SIMD[DType.uint8, 1]\n",
    "\n",
    "var x: Float16 = 0  # Float16 works like a \"typedef\""
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Like `var` variables, aliases obey scope, and you can use local aliases within\n",
    "functions as you'd expect."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Fully-bound, partially-bound, and unbound types\n",
    "\n",
    "A parametric type with its parameters specified is said to be _fully-bound_. \n",
    "That is, all of its parameters are bound to values. As mentioned before, you can\n",
    "only instantiate a fully-bound type (sometimes called a _concrete type_).\n",
    "\n",
    "However, parametric types can be _unbound_ or _partially bound_ in some\n",
    "contexts. For example, you can alias a partially-bound type to create a new type\n",
    "that requires fewer parameters:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "alias StringKeyDict = Dict[String, _]\n",
    "var b = StringKeyDict[UInt8]()\n",
    "b[\"answer\"] = 42"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here, `StringKeyDict` is a type alias for a `Dict` that takes `String` keys. The\n",
    "underscore `_` in the parameter list indicates that the second parameter,\n",
    "`V` (the value type), is unbound.\n",
    "You specify the `V` parameter later, when you use `StringKeyDict`.\n",
    "\n",
    "For example, given the following type:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "struct MyType[s: String, i: Int, i2: Int, b: Bool = True]:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It can appear in code in the following forms:\n",
    "\n",
    "- _Fully bound_, with all of its parameters specified:\n",
    "\n",
    "  ```mojo\n",
    "  MyType[\"Hello\", 3, 4, True]\n",
    "  ```\n",
    "\n",
    "- _Partially bound_, with *some but not all* of its parameters specified:\n",
    "\n",
    "  ```mojo\n",
    "  MyType[\"Hola\", _, _, True]\n",
    "  ```\n",
    "\n",
    "- _Unbound_, with no parameters specified:\n",
    "\n",
    "  ```mojo\n",
    "  MyType[_, _, _, _]\n",
    "  ```\n",
    "\n",
    "You can also use the star-underscore expression `*_` to unbind an arbitrary\n",
    "number of positional parameters at the end of a parameter\n",
    "list.\n",
    "\n",
    "```mojo\n",
    "# These two types are equivalent\n",
    "MyType[\"Hello\", *_]\n",
    "MyType[\"Hello\", _, _, _]\n",
    "```\n",
    "\n",
    "When a parameter is explicitly unbound with the `_` or `*_` expression, you\n",
    "**must** specify a value for that parameter to use the type. Any default value\n",
    "from the original type declaration is ignored.\n",
    "\n",
    "Partially-bound and unbound parametric types can be used in some contexts where\n",
    "the missing (unbound) parameters will be supplied later—such as in \n",
    "[aliases](#alias-named-parameter-expressions) and\n",
    "[automatically parameterized functions](#automatic-parameterization-of-functions).\n",
    "\n",
    "### Omitted parameters\n",
    "\n",
    "Mojo also supports an alternate format for unbound parameter where the parameter\n",
    "is simply omitted from the expression:\n",
    "\n",
    "```mojo\n",
    "# Partially bound\n",
    "MyType[\"Hi there\"]\n",
    "# Unbound\n",
    "MyType\n",
    "```\n",
    "\n",
    "This format differs from the explicit unbinding syntax described above in that\n",
    "the default values for omitted parameters are bound immediately. For example, \n",
    "the following expressions are equivalent: \n",
    "\n",
    "```mojo\n",
    "MyType[\"Hi there\"]\n",
    "# equivalent to\n",
    "MyType[\"Hi there\", _, _, True] # Uses the default value for `b`\n",
    "```\n",
    "\n",
    ":::note \n",
    "\n",
    "This format is currently supported for backwards compatibility. We intend to\n",
    "deprecate this format in the future in favor of the explicit unbinding syntax.\n",
    "\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## Automatic parameterization of functions\n",
    "\n",
    "Mojo  supports \"automatic\" parameterization of functions. If a function \n",
    "argument type is a \n",
    "[partially-bound or unbound type](#fully-bound-partially-bound-and-unbound-types),\n",
    "the unbound parameters are automatically added as input parameters on the \n",
    "function. This is easier to understand with an example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "float64\n",
      "4\n"
     ]
    }
   ],
   "source": [
    "fn print_params(vec: SIMD[*_]):\n",
    "    print(vec.type)\n",
    "    print(vec.size)\n",
    "\n",
    "var v = SIMD[DType.float64, 4](1.0, 2.0, 3.0, 4.0)\n",
    "print_params(v)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the above example, the `print_params` function is automatically \n",
    "parameterized. The `vec` argument takes an argument of type `SIMD[*_]`. This is\n",
    "an [unbound parameterized\n",
    "type](#fully-bound-partially-bound-and-unbound-types)—that is, it doesn't\n",
    "specify any parameter values for the type. Mojo treats the unbound parameters\n",
    "on `vec` as implicit parameters on the function. This is roughly equivalent to\n",
    "the following code, which includes _explicit_ input parameters:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn print_params[t: DType, s: Int](vec: SIMD[t, s]):\n",
    "    print(vec.type)\n",
    "    print(vec.size)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "When you call `print_params()` you must pass it a concrete instance of the \n",
    "`SIMD`  type—that is, one with all of its parameters specified, like \n",
    "`SIMD[DType.float64, 4]`. The Mojo compiler _infers_ the parameter \n",
    "values from the input argument.\n",
    "\n",
    "With a manually parameterized function, you can access the input parameters by\n",
    "name (for example, `t` and `s` in the previous example). For an\n",
    "automatically parameterized function, you can access the parameters as\n",
    "attributes on the argument (for example, `vec.type`). \n",
    "\n",
    "This ability to access a type's input parameters is not specific to \n",
    "automatically parameterized functions, you can use it anywhere. You can access \n",
    "the input parameters of a parameterized type as attributes on the type itself:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn on_type():\n",
    "    print(SIMD[DType.float32, 2].size) # prints 2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Or as attributes on an _instance_ of the type:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn on_instance():\n",
    "    var x = SIMD[DType.int32, 2](4, 8)\n",
    "    print(x.type) # prints int32"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can even use this syntax in the function's signature to define a \n",
    "function's arguments and return type based on an argument's parameters.\n",
    "For example, if you want your function to take two SIMD vectors with the same\n",
    "type and size, you can write code like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 0, 2, 0, 3, 0, 4, 0]\n"
     ]
    }
   ],
   "source": [
    "fn interleave(v1: SIMD, v2: __type_of(v1)) -> SIMD[v1.type, v1.size*2]:\n",
    "    var result = SIMD[v1.type, v1.size*2]()\n",
    "    for i in range(v1.size):\n",
    "        result[i*2] = SIMD[v1.type, 1](v1[i])\n",
    "        result[i*2+1] = SIMD[v1.type, 1](v2[i])\n",
    "    return result\n",
    "\n",
    "var a = SIMD[DType.int16, 4](1, 2, 3, 4)\n",
    "var b = SIMD[DType.int16, 4](0, 0, 0, 0)\n",
    "var c = interleave(a, b)\n",
    "print(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As shown in the example, you can use the magic  `__type_of(x)` call if you just want to match the type of an argument. In this case, it's more convenient and compact that writing the equivalent `SIMD[v1.type, v1.size]`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Automatic parameterization with partially-bound types\n",
    "\n",
    "Mojo also supports automatic parameterization: with [partially-bound\n",
    "parameterized types](#fully-bound-partially-bound-and-unbound-types) (that is,\n",
    "types with some but not all of the parameters specified).\n",
    "\n",
    "For example, suppose we have a `Fudge` struct with three parameters:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "@value\n",
    "struct Fudge[sugar: Int, cream: Int, chocolate: Int = 7](Stringable):\n",
    "    fn __str__(self) -> String:\n",
    "        var values = StaticIntTuple[3](sugar, cream, chocolate)\n",
    "        return str(\"Fudge\") + str(values)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can write a function that takes a `Fudge` argument with just one bound \n",
    "parameter (it's _partially bound_):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn eat(f: Fudge[5, *_]):\n",
    "    print(\"Ate \" + str(f))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `eat()` function takes a `Fudge` struct with the first parameter (`sugar`)\n",
    "bound to the value 5. The second and third parameters, `cream` and `chocolate`\n",
    "are unbound.\n",
    "\n",
    "The unbound `cream` and `chocolate` parameters become implicit input parameters\n",
    "on the `eat` function. In practice, this is roughly equivalent to writing:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn eat[cr: Int, ch: Int](f: Fudge[5, cr, ch]):\n",
    "    print(\"Ate \" + str(f))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In both cases, we can call the function by passing in an instance with the\n",
    "`cream` and `chocolate` parameters bound:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Ate Fudge(5, 5, 7)\n",
      "Ate Fudge(5, 8, 9)\n"
     ]
    }
   ],
   "source": [
    "eat(Fudge[5, 5, 7]())\n",
    "eat(Fudge[5, 8, 9]())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you try to pass in an argument with a `sugar` value other than 5,\n",
    "compilation fails, because it doesn't match the argument type:\n",
    "\n",
    "```mojo\n",
    "eat(Fudge[12, 5, 7]()) \n",
    "# ERROR: invalid call to 'eat': argument #0 cannot be converted from 'Fudge[12, 5, 7]' to 'Fudge[5, 5, 7]'\n",
    "```\n",
    "\n",
    "\n",
    "You can also explicitly unbind individual parameters. This gives you \n",
    "more freedom in specifying unbound parameters.\n",
    "\n",
    "For example, you might want to let the user specify values for `sugar` and \n",
    "`chocolate`, and leave `cream` constant. To do this, replace each unbound\n",
    "parameter value with a single underscore (`_`):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn devour(f: Fudge[_, 6, _]):\n",
    "    print(str(\"Devoured \") + str(f))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Again, the unbound parameters (`sugar` and `chocolate`) are added as implicit\n",
    "input parameters on the function. This version is roughly equivalent to the\n",
    "following code, where these two values are explicitly bound to the input \n",
    "parameters, `su` and `ch`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn devour[su: Int, ch: Int](f: Fudge[su, 6, ch]):\n",
    "    print(str(\"Devoured \") + str(f))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also specify parameters by keyword, or mix positional and keyword\n",
    "parameters, so the following function is roughly equivalent to the previous one:\n",
    "the first parameter, `sugar` is explicitly unbound with the underscore character.\n",
    "The `chocolate` parameter is unbound using the keyword syntax, `chocolate=_`. \n",
    "And `cream` is explicitly bound to the value 6:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn devour(f: Fudge[_, chocolate=_, cream=6]):\n",
    "    print(str(\"Devoured \") + str(f))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "All three versions of the `devour()` function work with the following calls:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Devoured Fudge(3, 6, 9)\n",
      "Devoured Fudge(4, 6, 8)\n"
     ]
    }
   ],
   "source": [
    "devour(Fudge[3, 6, 9]())\n",
    "devour(Fudge[4, 6, 8]())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Legacy syntax (omitted parameters)\n",
    "\n",
    "You can also specify an unbound or partially-bound type by omitting parameters: \n",
    "for example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn nibble(f: Fudge[5]):\n",
    "    print(\"Ate \" + str(f))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here, `Fudge[5]` works like `Fudge[5, *_]` **except** in the handling of parameters with default values. Instead of discarding the default value of\n",
    "`chocolate`, `Fudge[5]` binds the default value immediately, making it equivalent to: `Fudge[5, _, 7]`.\n",
    "\n",
    "\n",
    "This means that the following code won't compile with the previous definition for `nibble()` function, since it doesn't use the default value for `chocolate`:\n",
    "\n",
    "```mojo\n",
    "nibble(Fudge[5, 5, 9]())\n",
    "# ERROR: invalid call to 'eat': argument #0 cannot be converted from 'Fudge[5, 5, 9]' to 'Fudge[5, 5, 7]'\n",
    "```\n",
    "\n",
    ":::note TODO\n",
    "\n",
    "Support for omitting unbound parameters will eventually be deprecated in\n",
    "favor of explicitly unbound parameters using `_` and `*_`. \n",
    "\n",
    ":::\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Mojo",
   "language": "mojo",
   "name": "mojo-jupyter-kernel"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "mojo"
   },
   "file_extension": ".mojo",
   "mimetype": "text/x-mojo",
   "name": "mojo"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
